package cn.yjh.support;

import cn.yjh.annotation.RPCPublish;
import cn.yjh.annotation.RPCService;
import cn.yjh.exception.ServicesException;
import cn.yjh.utils.AnnotationElementUtils;
import cn.yjh.utils.ObjectUtils;
import cn.yjh.utils.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @DESC:
 * @AUTHOR: YouJinhua
 * @DATE: 2022-01-10 19:41:44
 * @VERSION: 1.0
 */
public interface ServiceRegister{

    Map<String, ServiceDefinition> serviceDefinitions = new ConcurrentHashMap<>(256);

    Map<String, ServiceDefinition> publishDefinitions = new ConcurrentHashMap<>(64);

    default Set<ServiceDefinition> getDefinitions(Class requiredType) {
        Set<ServiceDefinition> definitions = new HashSet<>();
        String defaultServiceName = formateServiceName(requiredType.getSimpleName());
        if(requiredType.isInterface()){
            ServiceDefinition publishDefinition = publishDefinitions.get(defaultServiceName);
            publishDefinition.aliasServiceNames.forEach(name -> {
                ServiceDefinition definition = serviceDefinitions.get(name);
                if (!definition.serviceClazz.isInterface()) {
                    definitions.add(definition);
                }
            });

        }else{
            definitions.add(serviceDefinitions.get(defaultServiceName));
        }
        return definitions;
    }

    default ServiceDefinition getDefinition(String serviceName) {
        ServiceDefinition definition = publishDefinitions.get(serviceName);
        if(definition == null){
            return serviceDefinitions.get(serviceName);
        }
        return definition;
    }

    default Set<ServiceDefinition> getDefinitions() {
        Set<ServiceDefinition> definitions = new HashSet<>();
        Set<Map.Entry<String, ServiceDefinition>> entries1 = serviceDefinitions.entrySet();
        Set<Map.Entry<String, ServiceDefinition>> entries2 = publishDefinitions.entrySet();
        entries1.forEach(entry -> {
            definitions.add(entry.getValue());
        });
        entries2.forEach(entry -> {
            definitions.add(entry.getValue());
        });
        return definitions;
    }

    default Set<String> getServiceNames() {
        Set<String> serviceNames = new HashSet<>();
        Set<String> publishNames = publishDefinitions.keySet();
        serviceNames.addAll(publishNames);
        serviceNames.addAll(serviceDefinitions.keySet());
        return serviceNames;
    }

    default String getServiceNames(Class requiredType) {
        return formateServiceName(requiredType.getSimpleName());
    }

    default void registerDefinition(Class clazz) {
        ServiceDefinition definition = new ServiceDefinition();
        definition.serviceClazz = clazz;
        String defaultServiceName = formateServiceName(clazz.getSimpleName());
        definition.defaultServiceName = defaultServiceName;
        List<String> aliasNames = new ArrayList<>();

        definition.aliasServiceNames = aliasNames;
        if(clazz.isInterface()){
            if(!publishDefinitions.containsKey(defaultServiceName)){
                definition.anno = AnnotationElementUtils.getMergeAnnotation(clazz, RPCPublish.class);
                publishDefinitions.put(defaultServiceName, definition);
            }
        }else{
            aliasNames.add(defaultServiceName);
            List<Class> publishTypes = new ArrayList<>();
            RPCService rpcService = AnnotationElementUtils.getMergeAnnotation(clazz, RPCService.class);
            definition.anno = rpcService;
            String aliasName = rpcService.serviceName();
            if (StringUtils.isNotEmpty(aliasName)) {
                aliasNames.add(aliasName);
            }
            definition.interfaceClazzs = clazz.getInterfaces();
            for (Class interfaceClazz : definition.interfaceClazzs) {
                aliasName = formateServiceName(interfaceClazz.getSimpleName());
                if(publishDefinitions.containsKey(aliasName)){
                    ServiceDefinition publishDefinition = publishDefinitions.get(aliasName);
                    publishDefinition.aliasServiceNames.add(defaultServiceName);
                }else{
                    registerDefinition(interfaceClazz);
                    ServiceDefinition publishDefinition = publishDefinitions.get(aliasName);
                    publishDefinition.aliasServiceNames.add(defaultServiceName);
                }

                if (interfaceClazz.isAnnotationPresent(RPCPublish.class)) {
                    publishTypes.add(interfaceClazz);
                }
            }
            definition.publishTypes = publishTypes;
        }
        aliasNames.forEach(name -> {
            if (serviceDefinitions.containsKey(name)) {
                throw new ServicesException("the service name：["+name+"] not only one");
            } else {
                serviceDefinitions.put(name, definition);
            }
        });
    }

    default boolean containsName(String name) {
        return getDefinitions().contains(name);
    }

    // 类名首字母小写
    default String formateServiceName(String name) {
        char[] chars = name.toCharArray();
        chars[0] = (char) (chars[0] | 0x20);
        return String.valueOf(chars);
    }

    void registerServiceDefinition();
}
